﻿/* 
* Copyright (c) 2009, The University of Texas at Austin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, 
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, 
* this list of conditions and the following disclaimer in the documentation and/or other materials 
* provided with the distribution.
*
* Neither the name of The University of Texas at Austin nor the names of its contributors may be 
* used to endorse or promote products derived from this software without specific prior written 
* permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Bio.Data.Interfaces;
using Bio.Data.Providers.Interfaces;
using Bio.Data.Providers.rCAD.Structure.Models;
using Bio.Data.Providers.rCAD.Structure.ViewModels;
using Bio.Data.Providers.rCAD.Structure.Views;
using JulMar.Windows.Interfaces;
using JulMar.Windows.Mvvm;

namespace Bio.Data.Providers.rCAD.Structure
{
    /// <summary>
    /// The rCAD Secondary Structure Model Data Provider architecture allows the user to open a set
    /// of RNA secondary structure models simultaneously. The entities collection is a set of shells
    /// and the loading of the secondary structure model is deferred until requested.
    /// </summary>
    class RcadSecondaryStructureProvider : IBioDataLoader<IStructureModelBioEntity>, IDisposable, IBioDataLoaderProperties
    {
        public readonly static string RCADRISTRUCTURE_CREATE_CONNECTION_UI = "RCADRISTRUCTURE_createConnection";

        private rcadDataContext _dc;
        private StructureModelSet _rCADStructureModelSet;
        private List<IStructureModelBioEntity> _entities;

        static RcadSecondaryStructureProvider()
        {
            IUIVisualizer uiVisualizer = ViewModel.ServiceProvider.Resolve<IUIVisualizer>();
            Debug.Assert(uiVisualizer != null);
            uiVisualizer.Register(RCADRISTRUCTURE_CREATE_CONNECTION_UI, typeof(CreateNewConnection));
        }

        /// <summary>
        /// This is used to initialize the BioDataLoader when it is first created.
        /// </summary>
        /// <param name="initData">String data</param>
        /// <returns>True/False success</returns>
        public bool Initialize(string initData)
        {
            _rCADStructureModelSet = PromptDbInfo();
            return _rCADStructureModelSet != null && _rCADStructureModelSet.IsValid;
        }

        /// <summary>
        /// This provides access to any initialization data used to create this loader.
        /// </summary>
        public string InitializationData
        {
            get { return _rCADStructureModelSet.ToString(); }
        }

        /// <summary>
        /// Returns a textual name for the data loader.
        /// </summary>
        public string DataDescription
        {
            get { return _rCADStructureModelSet.Name;  }
        }

        /// <summary>
        /// Returns whether this data stream is readonly or not.
        /// </summary>
        public bool IsReadOnly { get { return true; } }

        /// <summary>
        /// This event should be raised by the loader when it changes properties
        /// that affect the sequence of data.
        /// </summary>
        public event EventHandler PropertiesChanged = delegate { };

        public bool ChangeProperties()
        {
            return false;
        }

        public int Load()
        {
            string rCADConnectionString = _rCADStructureModelSet.Connection.BuildConnectionString();
            try
            {
                _dc = CreateDbContext(rCADConnectionString);

                _entities = new List<IStructureModelBioEntity>();

                // BUGBUG: if the # of sequences is > 2100 then we cannot generate the proper
                // query here since T-SQL doesn't support that many parameters.  So, here we'll group
                // it in batches of 2000.

                if (_rCADStructureModelSet.SelectedSet.Count > 2100)
                {
                    var queryGroups = _rCADStructureModelSet.SelectedSet.Select((id, index) => new { GroupID = index / 2000, id }).GroupBy(x => x.GroupID);
                    foreach (var singleGroup in queryGroups)
                    {
                        var thisGroup = singleGroup;
                        var strmodels = from smd in _dc.SequenceMains
                                        from tax in _dc.TaxonomyNamesOrdereds
                                        from taxName in _dc.TaxonomyNames
                                        where thisGroup.Select(x => x.id).Contains(smd.SeqID)
                                          && tax.TaxID == smd.TaxID
                                          && taxName.TaxID == smd.TaxID
                                        select new DbStructureModelBioEntity(_dc, smd.SeqID, taxName.ScientificName, _rCADStructureModelSet.AlignmentId);
                        _entities.AddRange(strmodels.Cast<IStructureModelBioEntity>());
                    }
                }
                else
                {
                    //We can do it in a single query, no paritioning necessary.
                    var strmodels = from smd in _dc.SequenceMains
                                    from tax in _dc.TaxonomyNamesOrdereds
                                    from taxName in _dc.TaxonomyNames
                                    where _rCADStructureModelSet.SelectedSet.Select(x => x).Contains(smd.SeqID)
                                      && tax.TaxID == smd.TaxID
                                      && taxName.TaxID == smd.TaxID
                                    select new DbStructureModelBioEntity(_dc, smd.SeqID, taxName.ScientificName, _rCADStructureModelSet.AlignmentId);
                    _entities.AddRange(strmodels.Cast<IStructureModelBioEntity>());
                }

                return _entities.Count;
            }
            catch (Exception ex)
            {
                IErrorVisualizer errorVisualizer = ViewModel.ServiceProvider.Resolve<IErrorVisualizer>();
                if (errorVisualizer != null)
                {
                    errorVisualizer.Show("Encountered error loading data", ex.Message);
                }

                _dc.Dispose();
                _dc = null;
            }

            return 0;
        }

        public IList<IStructureModelBioEntity> Entities
        {
            get { return _entities; }
        }

        IList IBioDataLoader.Entities
        {
            get { return _entities; }
        }

        public void Dispose()
        {
            _dc.Dispose();
            // Wipe the properties changed list.
            PropertiesChanged = delegate { };
        }

        private static StructureModelSet PromptDbInfo()
        {
            IUIVisualizer uiVisualizer = ViewModel.ServiceProvider.Resolve<IUIVisualizer>();
            Debug.Assert(uiVisualizer != null);

            var vm = new SetViewModel();
            if (uiVisualizer.ShowDialog(RCADRISTRUCTURE_CREATE_CONNECTION_UI, vm) == true)
            {
                return vm.Data;
            }

            return null;
        }

        internal static rcadDataContext CreateDbContext(string connectionString)
        {
            var dc = new rcadDataContext(connectionString);
            //if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["RcadDbLog"]))
                //dc.Log = Console.Out;
            return dc;
        }
    }
}
